#include "StdAfx.h"
#include "BlockingModelLib.h"


INetworkLib* BlockingModelLib::CreateComponent()
{
	return new CBlockingModelLib;
}

struct WorkerThreadDesc
{
	SOCKET				m_socket;
	CBlockingModelLib*	m_pNetworkLib;	
	WorkerThreadDesc(SOCKET m_socket
		, CBlockingModelLib& networkLib
		)
		: m_socket(m_socket)
		, m_pNetworkLib(&networkLib)					
	{

	}	
};

CBlockingModelLib::CBlockingModelLib(void)
{
}


CBlockingModelLib::~CBlockingModelLib(void)
{
}

bool CBlockingModelLib::Initialize( ON_CONNECT_FUNCTOR onConnect 
								, ON_ACCEPT_FUNCTOR onAccept
								, ON_DISCONNECT_FUNCTOR onDisconnect 
								, ON_RECV_FUNCTOR onRecv  
								)
{
	if (false == Helper::WSAStartup())
	{
		return false;
	}

	m_onConnectFunctor		= onConnect;
	m_onAcceptFunctor		= onAccept;
	m_onDisconnectFunctor	= onDisconnect;
	m_onRecvFunctor			= onRecv;
	return true;
}

bool CBlockingModelLib::Connect( const std::tstring& ip, WORD port )
{

	SOCKET connectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (connectSocket == INVALID_SOCKET) 
	{
		return false;
	}

	// Connect to server.
	SOCKADDR_IN sockaddr;	
	INT addressSize = 0;	
	Helper::WSAStringToAddress(ip, port, sockaddr, addressSize);
	
	int iResult = connect( connectSocket, (SOCKADDR*)&sockaddr, addressSize);
	if (iResult == SOCKET_ERROR) 
	{		
		return false;
	}

	DWORD connect_result = 0;

	if (false == createRecvWorkerThread(connectSocket))
	{
		connect_result = WSA_NOT_ENOUGH_MEMORY;
	}

	m_onConnectFunctor(connectSocket, connect_result);
	
	return true;
}

bool CBlockingModelLib::Send( SOCKET socket, IStreamBuffer& stream )
{
	do 
	{
		int iResult = send( socket, stream.GetRaw()+stream.GetReadOffset(), stream.GetLength()-stream.GetReadOffset(), 0 );
		if (iResult == SOCKET_ERROR) 
		{
			if (WSAEWOULDBLOCK != WSAGetLastError())
			{
				Disconnect(socket);
				return false;
			}
		}

		stream.Read(iResult);
	} while (stream.GetReadOffset() < stream.GetLength());
	stream.Release();

	return true;
}

bool CBlockingModelLib::Disconnect( SOCKET socket )
{
	shutdown(socket, SD_SEND);
	closesocket(socket);
	return true;
}

UINT __stdcall CBlockingModelLib::recvWorkerThread( void* pArguments )
{
	WorkerThreadDesc* pWorkerThreadDesc = (WorkerThreadDesc*)pArguments;

	while(1)
	{
		int iResult = 0;

		char	tempBuf[512];
		iResult = recv(pWorkerThreadDesc->m_socket, tempBuf, sizeof(tempBuf), 0);
		if ( iResult > 0 )
		{
			IStreamBuffer* pStreamBuffer = StreamBuffer::CreateComponent();
			pStreamBuffer->Write(tempBuf, iResult);
			pWorkerThreadDesc->m_pNetworkLib->m_onRecvFunctor(pWorkerThreadDesc->m_socket, pStreamBuffer);
		}
		else if ( iResult == 0 )
		{
			pWorkerThreadDesc->m_pNetworkLib->m_onDisconnectFunctor(pWorkerThreadDesc->m_socket);
			break;
		}
		else
		{
			if (WSAEWOULDBLOCK != WSAGetLastError())
			{
				pWorkerThreadDesc->m_pNetworkLib->m_onDisconnectFunctor(pWorkerThreadDesc->m_socket);
				break;
			}
		}
		
	}

	pWorkerThreadDesc->m_pNetworkLib->Disconnect(pWorkerThreadDesc->m_socket);
	delete pWorkerThreadDesc;

	return 0;
}

UINT CBlockingModelLib::acceptWorkerThread( void* pArguments )
{	
	WorkerThreadDesc* pWorkerThreadDesc = (WorkerThreadDesc*)pArguments;
	
	while(1)
	{
		SOCKET clientSocket = accept(pWorkerThreadDesc->m_socket, NULL, NULL);

		if (clientSocket == INVALID_SOCKET) 
		{
			continue;
		}

		pWorkerThreadDesc->m_pNetworkLib->m_onAcceptFunctor(clientSocket);

		pWorkerThreadDesc->m_pNetworkLib->createRecvWorkerThread(clientSocket);
	}

	pWorkerThreadDesc->m_pNetworkLib->Disconnect(pWorkerThreadDesc->m_socket);
	delete pWorkerThreadDesc;
	
	return 0;
}

bool CBlockingModelLib::Listen( const std::tstring& ip, WORD port, int backlog )
{
	SOCKET listenSocket = Helper::WSAListen(ip, port, backlog);
	if (listenSocket == INVALID_SOCKET)
	{
		return false;
	}

	WorkerThreadDesc* pArgment = new WorkerThreadDesc(listenSocket, *this);

	UINT threadID = 0;
	HANDLE hThread = (HANDLE)_beginthreadex(0, 0, CBlockingModelLib::acceptWorkerThread, pArgment, 0, &threadID);
	if (hThread == INVALID_HANDLE_VALUE)
	{
		Disconnect(listenSocket);
		return false;
	}	

	CloseHandle(hThread);
	return true;
}


void CBlockingModelLib::Release()
{
	Helper::WSACleanup();
	delete this;
}

bool CBlockingModelLib::createRecvWorkerThread( SOCKET socket )
{
	WorkerThreadDesc* pArgment = new WorkerThreadDesc(socket, *this);

	UINT threadID = 0;
	HANDLE hThread = (HANDLE)_beginthreadex(0, 0, CBlockingModelLib::recvWorkerThread, pArgment, 0, &threadID);
	if (hThread == INVALID_HANDLE_VALUE)
	{
		Disconnect(socket);
		return false;
	}

	CloseHandle(hThread);
	return true;
}
